//--------------------------------------------------------------------------------
// This file is a portion of the PracticeEngine Rendering Engine.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Ferdinand Li 
//--------------------------------------------------------------------------------

#include "PCH.h"
#include "DX11Renderer.h"
#include <d3dcompiler.h>
#include "DxUtils.h"
#include "PEMath.h"

using namespace PracEng;

void DX11Renderer::Initialize()
{
	HRESULT hr;
	m_CurrDeviceFlags = 0;
	m_CurrDriverType = D3D_DRIVER_TYPE_NULL;
#ifdef _DEBUG
	m_CurrDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE( driverTypes );

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	UINT numFeatureLevels = ARRAYSIZE( featureLevels );

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory( &sd, sizeof( sd ) );
	sd.BufferCount = 1;
	sd.BufferDesc.Width = m_BackbufferWidth;
	sd.BufferDesc.Height = m_BackbufferHeight;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = m_hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
	{
		m_CurrDriverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDeviceAndSwapChain( NULL, m_CurrDriverType, NULL, m_CurrDeviceFlags, featureLevels, numFeatureLevels,
			D3D11_SDK_VERSION, &sd, &m_pSwapChain, &m_pDevice, &m_CurrfeatureLevel, &m_pDeviceContext );
		if( SUCCEEDED( hr ) )
			break;
	}

	if (FAILED(hr))
	{
		return;
	}

	ID3D11Texture2D* pBackBuffer = NULL;
	TRACE_HR( m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer));
	TRACE_HR( m_pDevice->CreateRenderTargetView(pBackBuffer, NULL, &m_pRenderTargetView));
	pBackBuffer->Release();
	
	m_pDeviceContext->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL );

	D3D11_VIEWPORT vp;
	vp.Width = (FLOAT)m_BackbufferWidth;
	vp.Height = (FLOAT)m_BackbufferHeight;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	m_pDeviceContext->RSSetViewports( 1, &vp );

}

void DX11Renderer::Exit()
{
	if( m_pDeviceContext ) m_pDeviceContext->ClearState();

	SAFE_RELEASE( m_pRenderTargetView );
	SAFE_RELEASE( m_pSwapChain);
	SAFE_RELEASE( m_pDeviceContext );
	SAFE_RELEASE( m_pDevice );
}

ID3D11DeviceContext* DX11Renderer::GetDeviceContext() const
{
	return m_pDeviceContext;
}

ID3D11Device* DX11Renderer::GetDevice() const
{
	return m_pDevice;
}

IDXGIFactory* DX11Renderer::GetFactory() const
{
	return NULL;
}

D3D_FEATURE_LEVEL DX11Renderer::GetCurrentFeatureLevel() const
{
	return m_CurrfeatureLevel;
}

UINT DX11Renderer::GetCurrentDeviceFlags() const
{
	return m_CurrDeviceFlags;
}

D3D_DRIVER_TYPE DX11Renderer::GetCurrentDriverType() const
{
	return m_CurrDriverType;
}


void DX11Renderer::Present()
{
	m_pSwapChain->Present( 0, 0 );
}

void DX11Renderer::Tick()
{
	Present();
}

INT DX11Renderer::LoadShader(LPCWSTR FileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob **ppCode)
{
	HRESULT hr = S_OK;

	DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined(DEBUG) || defined(_DEBUG)
	dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob* pErrorBlob;
	hr = D3DCompileFromFile(FileName, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, szEntryPoint, szShaderModel, dwShaderFlags, 0, ppCode, &pErrorBlob);
	if (FAILED(hr) && pErrorBlob != 0 && pErrorBlob->GetBufferPointer() != 0)
#if defined( _DEBUG )
		OutputDebugStringA((LPCSTR)pErrorBlob->GetBufferPointer());
#endif 

	if (pErrorBlob) pErrorBlob->Release();

	return hr;
}

ID3D11RenderTargetView* DX11Renderer::GetRenderTargetView() const
{
	return m_pRenderTargetView;
}

HRESULT DX11Renderer::CreateTexture2D(const D3D11_TEXTURE2D_DESC* pDtd, const D3D11_SUBRESOURCE_DATA* pSubDat, ID3D11Texture2D** pTex2D)
{
	return m_pDevice->CreateTexture2D(pDtd, pSubDat, pTex2D);
}

HRESULT DX11Renderer::CreateRTView(ID3D11Resource* rt, const D3D11_RENDER_TARGET_VIEW_DESC* des, ID3D11RenderTargetView** RTV)
{
	return m_pDevice->CreateRenderTargetView(rt, des, RTV); 
}

HRESULT DX11Renderer::CreateDSView(ID3D11Resource* rt, const D3D11_DEPTH_STENCIL_VIEW_DESC* des, ID3D11DepthStencilView** DSV)
{
	return m_pDevice->CreateDepthStencilView(rt, des, DSV);
}

HRESULT DX11Renderer::CreateSRView(ID3D11Resource* rt, const D3D11_SHADER_RESOURCE_VIEW_DESC* des, ID3D11ShaderResourceView** SRV)
{
	return m_pDevice->CreateShaderResourceView(rt, des, SRV);
}
